 package javango.contrib.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javango.conf.Settings;
import javango.core.AnonymousUser;
import javango.core.servers.RequestProcessor;
import javango.http.HttpException;
import javango.http.HttpRequest;
import javango.http.HttpResponse;
import javango.http.SimpleHttpResponse;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Module;

public class Servlet extends HttpServlet implements Module {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 4173911300243443940L;

	private final static Log log = LogFactory.getLog(Servlet.class);
	
	Injector injector;
	
	// TODO Should Guice provide this giving the developer the flexability to create a new one or use a singleton
	// therefore getting rid of this property?
	RequestProcessor requestProcessor;
	
	protected Injector getInjector() throws ServletException {
		try {

            String guiceModule = this.getServletConfig().getInitParameter("GuiceModules");
            String settings = this.getServletConfig().getInitParameter("Settings");

            List<Module> moduleList = new ArrayList<Module>();
            moduleList.add(this);

            if (!StringUtils.isBlank(guiceModule)) {
            	for (String moduleName : guiceModule.split(";")) {
            		moduleList.add((Module)Class.forName(moduleName.trim()).newInstance());
            	}
        		return  com.google.inject.Guice.createInjector(moduleList);
            } else if (!StringUtils.isBlank(settings)) {
           		return ((Settings)Class.forName(settings.trim()).newInstance()).createInjector(moduleList);
            } else {
            	throw new ServletException("You must provide GuiceModules or Settings as a parameter to the servlet");
            }
            
		} catch (Exception e) {
			// TODO Instead of throwing servletexception add a single url (^.*$) to the list that shows the error message and stack.
			log.error(e,e);
			throw new ServletException(e);
		}
	}
	
	@Override
	public void init() throws ServletException {
		log.debug("Entering Servlet.init");
		super.init();
		injector = getInjector();
        requestProcessor = injector.getInstance(RequestProcessor.class);
	}

	
	public void configure(Binder binder) {
		binder.bind(Servlet.class).toInstance(this);
	}


	@Override
	public void destroy() {
		requestProcessor.stop();
		super.destroy();
	}

	public void service(final HttpServletRequest servletRequest, final HttpServletResponse servletResponse) throws IOException, ServletException {
		HttpRequest request = injector.getInstance(HttpRequest.class);
		try {			
			String context = StringUtils.trimToEmpty(servletRequest.getContextPath());
			String path = StringUtils.trimToEmpty(servletRequest.getRequestURI());
			path = path.substring(context.length()+1); // +1 to gobble up the trailing slash
			request.setPath(path);
			request.setContext(context);
			request.setParameterMap(servletRequest.getParameterMap());
			request.setMethod(servletRequest.getMethod());
			request.setSession(new HttpServletSession(servletRequest));
			
			if (servletRequest.getUserPrincipal() == null) {
				// TODO this user should not necessarly change from request to request (maybe).  only works if the user has cookies,  see how django handles.
				request.setUser(new AnonymousUser());
			} else {
				request.setUser(new PrincipalUser(servletRequest));
			}
			
			HttpResponse response = requestProcessor.service(request);
			response.render(servletRequest, servletResponse);
			
			// tell the request that we are done so it can notify any listeners.  good place to close database sessions, etc. 
			request.close();
		} catch (HttpException e) {
			log.error(e,e);
			try {
				request.close();
			} catch (HttpException e1) {
				log.error(e1,e1);
			}
			// TODO Pretty print the exception and stack trace,  request information, etc
			if (!injector.getInstance(Settings.class).isDebug()) {
				HttpResponse response = new SimpleHttpResponse("Unrecoverable error, please contact support");
				try {
					response.render(servletRequest, servletResponse);
				} catch (HttpException e2) {
					log.error(e2,e2);
					throw new ServletException(e);
				}
			} else {
				throw new ServletException(e);
				
			}
		}
	}
	
}
